项目地址:https://github.com/bumptech/glide
官方文档:http://bumptech.github.io/glide/
中文文档:https://muyangmin.github.io/glide-docs-cn/
原文链接:https://blog.csdn.net/guolin_blog/article/details/53939176
·
文章目录:
Glide 快速入门
Glide 源码分析执行流程
Glide 缓存机制
在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示:
虽说只有这简简单单的一行代码,但大家可能不知道的是,Glide 在背后帮我们默默执行了成吨的工作。
我们在平时使用 Glide 的时候格外地简单和方便,但是知其然也要知其所以然。那么今天我们就来解析一下 Glide 的源码,看看它在这些简单用法的背后,到底执行了多么复杂的工作。
with()
with() 方法是 Glide 类中的一组静态方法,它有好几个方法重载,我们来看一下 Glide 类中所有 with() 方法的方法重载:
可以看到,with() 方法的重载种类非常多,既可以传入 Activity,也可以传入 Fragment 或者是 Context。每一个 with() 方法重载的代码都非常简单,都是先调用 RequestManagerRetriever 的静态 get() 方法得到一个 RequestManagerRetriever 对象,这个静态 get() 方法就是一个单例实现,没什么需要解释的。然后再调用 RequestManagerRetriever 的实例 get() 方法,去获取 RequestManager 对象。
而 RequestManagerRetriever 的实例 get() 方法中的逻辑是什么样的呢?我们一起来看一看:
上述代码虽然看上去逻辑有点复杂,但是将它们梳理清楚后还是很简单的。RequestManagerRetriever 类中看似有很多个 get() 方法的重载,什么 Context 参数,Activity 参数,Fragment 参数等等,实际上只有两种情况而已,即传入 Application 类型的参数,和传入非 Application 类型的参数。
我们先来看传入 Application 参数的情况。如果在 Glide.with() 方法中传入的是一个 Application 对象,那么这里就会调用带有 Context 参数的 get() 方法重载,然后会在第 44 行调用 getApplicationManager() 方法来获取一个 RequestManager 对象。其实这是最简单的一种情况,因为 Application 对象的生命周期即应用程序的生命周期,因此 Glide 并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话, Glide 的加载也会同时终止。
接下来我们看传入非 Application 参数的情况。不管你在 Glide.with() 方法中传入的是 Activity、FragmentActivity、v4 包下的 Fragment、还是 app 包下的 Fragment,最终的流程都是一样的,那就是会向当前的 Activity 当中添加一个隐藏的 Fragment。具体添加的逻辑是在上述代码的第 117 行和第 141 行,分别对应的 app 包和 v4 包下的两种 Fragment 的情况。那么这里为什么要添加一个隐藏的 Fragment 呢?因为 Glide 需要知道加载的生命周期。很简单的一个道理,如果你在某个 Activity 上正在加载着一张图片,结果图片还没加载出来, Activity 就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是 Glide 并没有办法知道 Activity 的生命周期,于是 Glide 就使用了添加隐藏 Fragment 的这种小技巧,因为 Fragment 的生命周期和 Activity 是同步的,如果 Activity 被销毁了,Fragment 是可以监听到的,这样 Glide 就可以捕获这个事件并停止图片加载了。
这里额外再提一句,从第 48 行代码可以看出,如果我们是在非主线程当中使用的 Glide,那么不管你是传入的 Activity 还是 Fragment,都会被强制当成 Application 来处理。不过其实这就属于是在分析代码的细节了,本篇文章我们将会把目光主要放在 Glide 的主线工作流程上面,后面不会过多去分析这些细节方面的内容。
总体来说,第一个 with() 方法的源码还是比较好理解的。其实就是为了得到一个 RequestManager 对象而已,然后 Glide 会根据我们传入 with() 方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等着我们呢,接下来我们开始分析第二步,load() 方法。
load()
由于 with() 方法返回的是一个 RequestManager 对象,那么很容易就能想到,load() 方法是在 RequestManager 类当中的,所以说我们首先要看的就是 RequestManager 这个类。不过在上一篇文章中我们学过,Glide 是支持图片 URL 字符串、图片本地路径等等加载形式的,因此 RequestManager 中也有很多个 load() 方法的重载。但是这里我们不可能把每个 load() 方法的重载都看一遍,因此我们就只选其中一个加载图片 URL 字符串的 load() 方法来进行研究。
RequestManager 类的简化代码如下所示:
RequestManager 类的代码是非常多的,但是经过我这样简化之后,看上去就比较清爽了。在我们只探究加载图片 URL 字符串这一个 load() 方法的情况下,那么比较重要的方法就只剩下上述代码中的这三个方法。
那么我们先来看 load() 方法,这个方法中的逻辑是非常简单的,只有一行代码,就是先调用了 fromString() 方法,再调用 load() 方法,然后把传入的图片 URL 地址传进去。而 fromString() 方法也极为简单,就是调用了 loadGeneric() 方法,并且指定参数为 String.class,因为 load() 方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在 loadGeneric() 方法中进行的了。
其实 loadGeneric() 方法也没几行代码,这里分别调用了 Glide.buildStreamModelLoader() 方法和 Glide.buildFileDescriptorModelLoader() 方法来获得 ModelLoader 对象。ModelLoader 对象是用于加载图片的,而我们给 load() 方法传入不同类型的参数,这里也会得到不同的 ModelLoader 对象。不过 buildStreamModelLoader() 方法内部的逻辑还是蛮复杂的,这里就不展开介绍了,要不然篇幅实在收不住,感兴趣的话你可以自己研究。由于我们刚才传入的参数是 String.class,因此最终得到的是 StreamStringLoader 对象,它是实现了 ModelLoader 接口的。
最后我们可以看到,loadGeneric() 方法是要返回一个 DrawableTypeRequest 对象的,因此在 loadGeneric() 方法的最后又去 new 了一个 DrawableTypeRequest 对象,然后把刚才获得的 ModelLoader 对象,还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就不解释了,我们只看主线流程。
那么这个 DrawableTypeRequest 的作用是什么呢?我们来看下它的源码,如下所示:
这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了 asBitmap() 和 asGif() 这两个方法。这两个方法分别是用于强制指定加载静态图片和动态图片。而从源码中可以看出,它们分别又创建了一个 BitmapTypeRequest 和 GifTypeRequest,如果没有进行强制指定的话,那默认就是使用 DrawableTypeRequest。
好的,那么我们再回到 RequestManager 的 load() 方法中。刚才已经分析过了,fromString() 方法会返回一个 DrawableTypeRequest 对象,接下来会调用这个对象的 load() 方法,把图片的 URL 地址传进去。但是我们刚才看到了,DrawableTypeRequest 中并没有 load() 方法,那么很容易就能猜想到,load() 方法是在父类当中的。
DrawableTypeRequest 的父类是 DrawableRequestBuilder,我们来看下这个类的源码:
DrawableRequestBuilder 中有很多个方法,这些方法其实就是 Glide 绝大多数的 API 了。里面有不少我们在上篇文章中已经用过了,比如说 placeholder() 方法、error() 方法、diskCacheStrategy() 方法、override() 方法等。当然还有很多暂时还没用到的 API,我们会在后面的文章当中学习。
到这里,第二步 load() 方法也就分析结束了。为什么呢?因为你会发现 DrawableRequestBuilder 类中有一个 into() 方法(上述代码第 220 行),也就是说,最终 load() 方法返回的其实就是一个 DrawableTypeRequest 对象。那么接下来我们就要进行第三步了,分析 into() 方法中的逻辑。
into()
如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为 into() 方法也是整个 Glide 图片加载流程中逻辑最复杂的地方。
不过从刚才的代码来看,into() 方法中并没有任何逻辑,只有一句 super.into(view)。那么很显然,into() 方法的具体逻辑都是在 DrawableRequestBuilder 的父类当中了。
DrawableRequestBuilder 的父类是 GenericRequestBuilder,我们来看一下 GenericRequestBuilder 类中的 into() 方法,如下所示:
这里前面一大堆的判断逻辑我们都可以先不用管,等到后面文章讲 transform 的时候会再进行解释,现在我们只需要关注最后一行代码。最后一行代码先是调用了 glide.buildImageViewTarget() 方法,这个方法会构建出一个 Target 对象,Target 对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码:
这里其实又是调用了 ImageViewTargetFactory 的 buildTarget() 方法,我们继续跟进去,代码如下所示:
可以看到,在 buildTarget() 方法中会根据传入的 class 参数来构建不同的 Target 对象。那如果你要分析这个 class 参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳理清楚。这个 class 参数其实基本上只有两种情况,如果你在使用 Glide 加载图片的时候调用了 asBitmap() 方法,那么这里就会构建出 BitmapImageViewTarget 对象,否则的话构建的都是 GlideDrawableImageViewTarget 对象。至于上述代码中的 DrawableImageViewTarget 对象,这个通常都是用不到的,我们可以暂时不用管它。
也就是说,通过 glide.buildImageViewTarget() 方法,我们构建出了一个 GlideDrawableImageViewTarget 对象。那现在回到刚才 into() 方法的最后一行,可以看到,这里又将这个参数传入到了 GenericRequestBuilder 另一个接收 Target 对象的 into() 方法当中了。我们来看一下这个 into() 方法的源码:
这里我们还是只抓核心代码,其实只有两行是最关键的,第 15 行调用 buildRequest() 方法构建出了一个 Request 对象,还有第 18 行来执行这个 Request。
Request 是用来发出加载图片请求的,它是 Glide 中非常关键的一个组件。我们先来看 buildRequest() 方法是如何构建 Request 对象的:
可以看到,buildRequest() 方法的内部其实又调用了 buildRequestRecursive() 方法,而 buildRequestRecursive() 方法中的代码虽然有点长,但是其中 90% 的代码都是在处理缩略图的。如果我们只追主线流程的话,那么只需要看第 47 行代码就可以了。这里调用了 obtainRequest() 方法来获取一个 Request 对象,而 obtainRequest() 方法中又去调用了 GenericRequest 的 obtain() 方法。注意这个 obtain() 方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么 placeholderId、errorPlaceholder、diskCacheStrategy 等等。因此,我们就有理由猜测,刚才在 load()方法中调用的所有 API,其实都是在这里组装到 Request 对象当中的。那么我们进入到这个 GenericRequest 的 obtain() 方法瞧一瞧:
可以看到,这里在第 33 行去 new 了一个 GenericRequest 对象,并在最后一行返回,也就是说,obtain() 方法实际上获得的就是一个 GenericRequest 对象。另外这里又在第 35 行调用了 GenericRequest 的 init(),里面主要就是一些赋值的代码,将传入的这些参数赋值到 GenericRequest 的成员变量当中,我们就不再跟进去看了。
好,那现在解决了构建 Request 对象的问题,接下来我们看一下这个 Request 对象又是怎么执行的。回到刚才的 into() 方法,你会发现在第 18 行调用了 requestTracker.runRequest() 方法来去执行这个 Request,那么我们跟进去瞧一瞧,如下所示:
这里有一个简单的逻辑判断,就是先判断 Glide 当前是不是处理暂停状态,如果不是暂停状态就调用 Request 的 begin() 方法来执行 Request,否则的话就先将 Request 添加到待执行队列里面,等暂停状态解除了之后再执行。
暂停请求的功能仍然不是这篇文章所关心的,这里就直接忽略了,我们重点来看这个 begin() 方法。由于当前的 Request 对象是一个 GenericRequest,因此这里就需要看 GenericRequest 中的 begin() 方法了,如下所示:
这里我们来注意几个细节,首先如果 model 等于 null,model 也就是我们在第二步 load() 方法中传入的图片 URL 地址,这个时候会调用 onException() 方法。如果你跟到 onException() 方法里面去看看,你会发现它最终会调用到一个 setErrorPlaceholder() 当中,如下所示:
这个方法中会先去获取一个 error 的占位图,如果获取不到的话会再去获取一个 loading 占位图,然后调用 target.onLoadFailed() 方法并将占位图传入。那么 onLoadFailed() 方法中做了什么呢?我们看一下:
很简单,其实就是将这张 error 占位图显示到 ImageView 上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才 begin() 方法的第 15 行,你会发现它又调用了一个 target.onLoadStarted() 方法,并传入了一个 loading 占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的 placeholder() 和 error() 这两个占位图 API 底层的实现原理。
好,那么我们继续回到 begin() 方法。刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在 begin() 方法的第 10 行和第 12 行。这里要分两种情况,一种是你使用了 override() API 为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第 10 行代码,调用 onSizeReady() 方法。如果没指定的话,就会执行第 12 行代码,调用 target.getSize() 方法。这个 target.getSize() 方法的内部会根据 ImageView 的 layout_width 和 layout_height 值做一系列的计算,来算出图片应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用 onSizeReady() 方法。也就是说,不管是哪种情况,最终都会调用到 onSizeReady() 方法,在这里进行下一步操作。那么我们跟到这个方法里面来:
从这里开始,真正复杂的地方来了,我们需要慢慢进行分析。先来看一下,在第12行调用了 loadProvider.getModelLoader() 方法,那么我们第一个要搞清楚的就是,这个 loadProvider 是什么?要搞清楚这点,需要先回到第二步的 load() 方法当中。还记得 load() 方法是返回一个 DrawableTypeRequest 对象吗?刚才我们只是分析了 DrawableTypeRequest 当中的 asBitmap() 和 asGif() 方法,并没有仔细看它的构造函数,现在我们重新来看一下 DrawableTypeRequest类的构造函数:
可以看到,这里在第 29 行,也就是构造函数中,调用了一个 buildProvider() 方法,并把 streamModelLoader 和 fileDescriptorModelLoader 等参数传入到这个方法中,这两个 ModelLoader 就是之前在 loadGeneric() 方法中构建出来的。
那么我们再来看一下 buildProvider() 方法里面做了什么,在第 16 行调用了 glide.buildTranscoder() 方法来构建一个 ResourceTranscoder,它是用于对图片进行转码的,由于 ResourceTranscoder 是一个接口,这里实际会构建出一个 GifBitmapWrapperDrawableTranscoder 对象。
接下来在第 18 行调用了 glide.buildDataProvider() 方法来构建一个 DataLoadProvider,它是用于对图片进行编解码的,由于 DataLoadProvider 是一个接口,这里实际会构建出一个 ImageVideoGifDrawableLoadProvider 对象。
然后在第 20 行,new 了一个 ImageVideoModelLoader 的实例,并把之前 loadGeneric() 方法中构建的两个 ModelLoader 封装到了 ImageVideoModelLoader 当中。
最后,在第 22 行,new 出一个 FixedLoadProvider,并把刚才构建的出来的 GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider 都封装进去,这个也就是 onSizeReady() 方法中的 loadProvider 了。
好的,那么我们回到 onSizeReady() 方法中,在 onSizeReady() 方法的第 12 行和第 18 行,分别调用了 loadProvider 的 getModelLoader() 方法和 getTranscoder() 方法,那么得到的对象也就是刚才我们分析的 ImageVideoModelLoader 和 GifBitmapWrapperDrawableTranscoder 了。而在第 13 行,又调用了 ImageVideoModelLoader 的 getResourceFetcher() 方法,这里我们又需要跟进去瞧一瞧了,代码如下所示:
可以看到,在第 20 行会先调用 streamLoader.getResourceFetcher() 方法获取一个 DataFetcher,而这个 streamLoader 其实就是我们在 loadGeneric() 方法中构建出的 StreamStringLoader,调用它的 getResourceFetcher() 方法会得到一个 HttpUrlFetcher 对象。然后在第 28 行 new 出了一个 ImageVideoFetcher 对象,并把获得的 HttpUrlFetcher 对象传进去。也就是说,ImageVideoModelLoader 的 getResourceFetcher() 方法得到的是一个 ImageVideoFetcher。
那么我们再次回到 onSizeReady() 方法,在 onSizeReady() 方法的第 23 行,这里将刚才获得的 ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder 等等一系列的值一起传入到了 Engine 的 load() 方法当中。接下来我们就要看一看,这个 Engine 的 load() 方法当中,到底做了什么?代码如下所示:
load() 方法中的代码虽然有点长,但大多数的代码都是在处理缓存的。关于缓存的内容我们会在下一篇文章当中学习,现在只需要从第 45 行看起就行。这里构建了一个 EngineJob,它的主要作用就是用来开启线程的,为后面的异步加载图片做准备。接下来第 46 行创建了一个 DecodeJob 对象,从名字上来看,它好像是用来对图片进行解码的,但实际上它的任务十分繁重,待会我们就知道了。继续往下看,第 48 行创建了一个 EngineRunnable 对象,并且在 51 行调用了 EngineJob 的 start() 方法来运行 EngineRunnable 对象,这实际上就是让 EngineRunnable 的 run() 方法在子线程当中执行了。那么我们现在就可以去看看 EngineRunnable 的 run() 方法里做了些什么,如下所示:
这个方法中的代码并不多,但我们仍然还是要抓重点。在第 9 行,这里调用了一个 decode() 方法,并且这个方法返回了一个 Resource 对象。看上去所有的逻辑应该都在这个 decode() 方法执行的了,那我们跟进去瞧一瞧:
decode() 方法中又分了两种情况,从缓存当中去 decode 图片的话就会执行 decodeFromCache(),否则的话就执行 decodeFromSource()。本篇文章中我们不讨论缓存的情况,那么就直接来看 decodeFromSource() 方法的代码吧,如下所示:
这里又调用了 DecodeJob 的 decodeFromSource() 方法。刚才已经说了,DecodeJob 的任务十分繁重,我们继续跟进看一看吧:
主要的方法就这些,我都帮大家提取出来了。那么我们先来看一下 decodeFromSource() 方法,其实它的工作分为两部,第一步是调用 decodeSource() 方法来获得一个 Resource 对象,第二步是调用 transformEncodeAndTranscode() 方法来处理这个 Resource 对象。
那么我们先来看第一步,decodeSource() 方法中的逻辑也并不复杂,首先在第 14 行调用了 fetcher.loadData() 方法。那么这个 fetcher 是什么呢?其实就是刚才在 onSizeReady() 方法中得到的 ImageVideoFetcher 对象,这里调用它的 loadData() 方法,代码如下所示:
可以看到,在 ImageVideoFetcher 的 loadData() 方法的第 6 行,这里又去调用了 streamFetcher.loadData() 方法,那么这个 streamFetcher 是什么呢?自然就是刚才在组装 ImageVideoFetcher 对象时传进来的 HttpUrlFetcher 了。因此这里又会去调用 HttpUrlFetcher 的 loadData() 方法,那么我们继续跟进去瞧一瞧:
经过一层一层地跋山涉水,我们终于在这里找到网络通讯的代码了!之前有朋友跟我讲过,说 Glide 的源码实在是太复杂了,甚至连网络请求是在哪里发出去的都找不到。我们也是经过一段一段又一段的代码跟踪,终于把网络请求的代码给找出来了,实在是太不容易了。
不过也别高兴得太早,现在离最终分析完还早着呢。可以看到, loadData() 方法只是返回了一个 InputStream,服务器返回的数据连读都还没开始读呢。所以我们还是要静下心来继续分析,回到刚才 ImageVideoFetcher 的 loadData() 方法中,在这个方法的最后一行,创建了一个 ImageVideoWrapper 对象,并把刚才得到的 InputStream 作为参数传了进去。
然后我们回到再上一层,也就是 DecodeJob 的 decodeSource() 方法当中,在得到了这个 ImageVideoWrapper 对象之后,紧接着又将这个对象传入到了 decodeFromSourceData() 当中,来去解码这个对象。decodeFromSourceData() 方法的代码如下所示:
可以看到,这里在第 7 行调用了 loadProvider.getSourceDecoder().decode() 方法来进行解码。loadProvider 就是刚才在 onSizeReady() 方法中得到的 FixedLoadProvider,而 getSourceDecoder() 得到的则是一个 GifBitmapWrapperResourceDecoder 对象,也就是要调用这个对象的 decode() 方法来对图片进行解码。那么我们来看下 GifBitmapWrapperResourceDecoder 的代码:
首先,在 decode() 方法中,又去调用了另外一个 decode() 方法的重载。然后在第 23 行调用了 decodeStream() 方法,准备从服务器返回的流当中读取数据。decodeStream() 方法中会先从流中读取 2 个字节的数据,来判断这张图是 GIF 图还是普通的静图,如果是 GIF 图就调用 decodeGifWrapper() 方法来进行解码,如果是普通的静图就用调用 decodeBitmapWrapper() 方法来进行解码。这里我们只分析普通静图的实现流程,GIF 图的实现有点过于复杂了,无法在本篇文章当中分析。
然后我们来看一下 decodeBitmapWrapper() 方法,这里在第 52 行调用了 bitmapDecoder.decode() 方法。这个 bitmapDecoder 是一个 ImageVideoBitmapDecoder 对象,那么我们来看一下它的代码,如下所示:
代码并不复杂,在第 14 行先调用了 source.getStream() 来获取到服务器返回的 InputStream,然后在第 17 行调用 streamDecoder.decode() 方法进行解码。streamDecode 是一个 StreamBitmapDecoder 对象,那么我们再来看这个类的源码,如下所示:
可以看到,它的 decode() 方法又去调用了 Downsampler 的 decode() 方法。接下来又到了激动人心的时刻了,Downsampler 的代码如下所示:
可以看到,对服务器返回的 InputStream 的读取,以及对图片的加载全都在这里了。当然这里其实处理了很多的逻辑,包括对图片的压缩,甚至还有旋转、圆角等逻辑处理,但是我们目前只需要关注主线逻辑就行了。decode() 方法执行之后,会返回一个 Bitmap 对象,那么图片在这里其实也就已经被加载出来了,剩下的工作就是如果让这个 Bitmap 显示到界面上,我们继续往下分析。
回到刚才的 StreamBitmapDecoder 当中,你会发现,它的 decode() 方法返回的是一个 Resource
BitmapResource 的源码也非常简单,经过这样一层包装之后,如果我还需要获取 Bitmap,只需要调用 Resource
然后我们需要一层层继续向上返回,StreamBitmapDecoder 会将值返回到 ImageVideoBitmapDecoder 当中,而 ImageVideoBitmapDecoder 又会将值返回到 GifBitmapWrapperResourceDecoder 的 decodeBitmapWrapper() 方法当中。由于代码隔得有点太远了,我重新把 decodeBitmapWrapper() 方法的代码贴一下:
可以看到,decodeBitmapWrapper() 方法返回的是一个 GifBitmapWrapper 对象。因此,这里在第 5 行,又将 Resource
还是比较简单的,就是分别对 gifResource 和 bitmapResource 做了一层封装而已,相信没有什么解释的必要。
然后这个 GifBitmapWrapper 对象会一直向上返回,返回到 GifBitmapWrapperResourceDecoder 最外层的 decode() 方法的时候,会对它再做一次封装,如下所示:
可以看到,这里在第 11 行,又将 GifBitmapWrapper 封装到了一个 GifBitmapWrapperResource 对象当中,最终返回的是一个 Resource
经过这一层的封装之后,我们从网络上得到的图片就能够以 Resource 接口的形式返回,并且还能同时处理 Bitmap 图片和 GIF 图片这两种情况。
那么现在我们可以回到 DecodeJob 当中了,它的 decodeFromSourceData() 方法返回的是一个 Resource
刚才我们就是从这里跟进到 decodeSource() 方法当中,然后执行了一大堆一大堆的逻辑,最终得到了这个 Resource
首先,这个方法开头的几行transform还有cache,这都是我们后面才会学习的东西,现在不用管它们就可以了。需要注意的是第9行,这里调用了一个transcode()方法,就把Resource
而 transcode() 方法中又是调用了 transcoder 的 transcode() 方法,那么这个 transcoder 是什么呢?其实这也是 Glide 源码特别难懂的原因之一,就是它用到的很多对象都是很早很早之前就初始化的,在初始化的时候你可能完全就没有留意过它,因为一时半会根本就用不着,但是真正需要用到的时候你却早就记不起来这个对象是从哪儿来的了。
那么这里我来提醒一下大家吧,在第二步 load() 方法返回的那个 DrawableTypeRequest 对象,它的构建函数中去构建了一个 FixedLoadProvider 对象,然后我们将三个参数传入到了 FixedLoadProvider 当中,其中就有一个 GifBitmapWrapperDrawableTranscoder 对象。后来在 onSizeReady() 方法中获取到了这个参数,并传递到了 Engine 当中,然后又由 Engine 传递到了 DecodeJob 当中。因此,这里的 transcoder 其实就是这个 GifBitmapWrapperDrawableTranscoder 对象。那么我们来看一下它的源码:
这里我来简单解释一下,GifBitmapWrapperDrawableTranscoder 的核心作用就是用来转码的。因为 GifBitmapWrapper 是无法直接显示到 ImageView 上面的,只有 Bitmap 或者 Drawable 才能显示到 ImageView 上。因此,这里的 transcode() 方法先从 Resource
接下来做了一个判断,如果 Resource
这里在第 15 行又进行了一次转码,是调用的 GlideBitmapDrawableTranscoder 对象的 transcode() 方法,代码如下所示:
可以看到,这里 new 出了一个 GlideBitmapDrawable 对象,并把 Bitmap 封装到里面。然后对 GlideBitmapDrawable 再进行一次封装,返回一个 Resource
现在再返回到 GifBitmapWrapperDrawableTranscoder 的 transcode() 方法中,你会发现它们的类型就一致了。因为不管是静图的 Resource
那么我们继续回到 DecodeJob当中,它的 decodeFromSource() 方法得到了 Resource
也就是说,经过第 9 行 decode() 方法的执行,我们最终得到了这个 Resource
这个 manager 就是 EngineJob 对象,因此这里实际上调用的是 EngineJob 的 onResourceReady() 方法,代码如下所示:
可以看到,这里在 onResourceReady() 方法使用 Handler 发出了一条 MSG_COMPLETE 消息,那么在 MainThreadCallback 的 handleMessage() 方法中就会收到这条消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新 UI 了。
然后在第 72 行调用了 handleResultOnMainThread() 方法,这个方法中又通过一个循环,调用了所有 ResourceCallback 的 onResourceReady() 方法。那么这个 ResourceCallback 是什么呢?答案在 addCallback() 方法当中,它会向 cbs 集合中去添加 ResourceCallback。那么这个 addCallback() 方法又是哪里调用的呢?其实调用的地方我们早就已经看过了,只不过之前没有注意,现在重新来看一下 Engine 的 load() 方法,如下所示:
这次把目光放在第 18 行上面,看到了吗?就是在这里调用的 EngineJob 的 addCallback() 方法来注册的一个 ResourceCallback。那么接下来的问题就是,Engine.load() 方法的 ResourceCallback 参数又是谁传过来的呢?这就需要回到 GenericRequest 的 onSizeReady() 方法当中了,我们看到 ResourceCallback 是 load() 方法的最后一个参数,那么在 onSizeReady() 方法中调用 load() 方法时传入的最后一个参数是什么?代码如下所示:
请将目光锁定在第 29 行的最后一个参数,this。没错,就是 this。GenericRequest 本身就实现了 ResourceCallback 的接口,因此 EngineJob 的回调最终其实就是回调到了 GenericRequest 的 onResourceReady() 方法当中了,代码如下所示:
这里有两个 onResourceReady() 方法,首先在第一个 onResourceReady() 方法当中,调用 resource.get() 方法获取到了封装的图片对象,也就是 GlideBitmapDrawable 对象,或者是 GifDrawable 对象。然后将这个值传入到了第二个 onResourceReady() 方法当中,并在第 36 行调用了 target.onResourceReady() 方法。
那么这个 target 又是什么呢?这个又需要向上翻很久了,在第三步 into() 方法的一开始,我们就分析了在 into() 方法的最后一行,调用了 glide.buildImageViewTarget() 方法来构建出一个 Target,而这个 Target 就是一个 GlideDrawableImageViewTarget 对象。
那么我们去看 GlideDrawableImageViewTarget 的源码就可以了,如下所示:
在 GlideDrawableImageViewTarget 的 onResourceReady() 方法中做了一些逻辑处理,包括如果是 GIF 图片的话,就调用 resource.start() 方法开始播放图片,但是好像并没有看到哪里有将 GlideDrawable 显示到 ImageView 上的逻辑。
确实没有,不过父类里面有,这里在第 25 行调用了 super.onResourceReady() 方法,GlideDrawableImageViewTarget 的父类是 ImageViewTarget,我们来看下它的代码吧:
可以看到,在 ImageViewTarget 的 onResourceReady() 方法当中调用了 setResource() 方法,而 ImageViewTarget 的 setResource() 方法是一个抽象方法,具体的实现还是在子类那边实现的。
那子类的 setResource() 方法是怎么实现的呢?回头再来看一下 GlideDrawableImageViewTarget 的 setResource() 方法,没错,调用的 view.setImageDrawable() 方法,而这个 view 就是 ImageView。代码执行到这里,图片终于也就显示出来了。
那么,我们对 Glide 执行流程的源码分析,到这里也终于结束了。
真是好长的一篇文章,这也可能是我目前所写过的最长的一篇文章了。如果你之前没有读过 Glide 的源码,真的很难相信,这短短一行代码: